#include <string.h>
#include <os_mem.h>
#include <driver_efuse.h>
#include <driver_plf.h>
#include <driver_flash.h>
#include <driver_adc.h>
#include <driver_wdt.h>

#include "stdc.h"
#include "std_type.h"
#include "nonos.h"
#include "mcu.h"
#include "sys_clock.h"

uint32_t getBeU32(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return MAKE_DWORD(buf[0], buf[1], buf[2], buf[3]);
}

uint16_t getBeU16(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return MAKE_WORD(buf[0], buf[1]);
}

uint32_t getLeU32(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return MAKE_DWORD(buf[3], buf[2], buf[1], buf[0]);
}

uint16_t getLeU16(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return MAKE_WORD(buf[1], buf[0]);
}

void mcu_init() {
    NVIC_SetPriority(PCB_SYSTICK_TIMER_IRQn, 1);
	NVIC_SetPriority(PCB_US_TIMER_IRQn, 1);
	// 初始化系统滴答定时器
	mcu_systick_timer_init();
}

static struct chip_unique_id_t chip_id;
	
uint8_t* mcu_get_chip_id(void) {
	efuse_get_chip_unique_id(&chip_id);
	return chip_id.unique_id;
}

bool mcu_is_in_flash(uint32_t addr, uint32_t len) {
	return addr >= MCU_FLASH_BEGIN_ADDRESS && addr + len < MCU_FLASH_END_ADDRESS;
}

uint32_t mcu_flash_page_addr(uint32_t addr) {
	return addr - (addr % MCU_FLASH_PAGE_SIZE);
}

uint32_t mcu_flash_write(uint32_t headAddr, uint32_t len, void *payload) {
	if(!len || !payload || !mcu_is_in_flash(headAddr, len)) { return 0; }

	// !!! 注意：写 Flash 过程中，禁止中断 !!!
	OSCritical cs;
	osCriticalEnter(cs); // {{{
	// 分配页缓存
	uint8_t *cache = osMalloc(MCU_FLASH_PAGE_SIZE);
	if(!cache) { osCriticalLeave(cs); return 0; }
	
	// 尾部地址
	uint32_t tailAddr = headAddr + len;
	// 缓存数据游标
	uint8_t *pc = cache;
	// 净荷数据游标
	uint8_t *pp = payload;
	
	// 数据头所在的目标页地址
	uint32_t headPageAddr = mcu_flash_page_addr(headAddr);
	// 数据头所在的目标页中有多少 dummy 数据
	uint32_t headDummy = headAddr - headPageAddr;
	// 数据头所在的目标页中能存放多少数据
	uint32_t headPayload = MCU_FLASH_PAGE_SIZE - headDummy;
	
	// 数据尾所在的目标页地址
	uint32_t tailPageAddr = mcu_flash_page_addr(tailAddr);
	// 数据尾所在的目标页中能存放多少数据
	uint32_t tailPayload = tailAddr - tailPageAddr;
	// 数据尾所在的目标页中有多少 dummy 数据
	uint32_t tailDummy = MCU_FLASH_PAGE_SIZE - tailPayload;
	
	if(headPageAddr == tailPageAddr) {
		// 头尾在同一页中，也就是说一页就可以写完
		// 读入头部的 dummy 数据到页缓存
		memcpy(pc, (void*)headPageAddr, headDummy); pc += headDummy;
		// 净荷写入页缓存
		memcpy(pc, pp, len); pc += len; pp += len;
		// 读入尾部的 dummy 数据到页缓存
		memcpy(pc, (void*)tailAddr, tailDummy); pc += tailDummy;
		// 擦除
		flash_erase(headPageAddr - MCU_FLASH_BEGIN_ADDRESS, MCU_FLASH_PAGE_SIZE);
		// 写入缓存
		flash_write(headPageAddr - MCU_FLASH_BEGIN_ADDRESS, MCU_FLASH_PAGE_SIZE, cache); pc = cache;
	} else {
		// 写入头部页
		// 读入头部的 dummy 数据到页缓存
		memcpy(pc, (void*)headPageAddr, headDummy); pc += headDummy;
		// 净荷头部写入页缓存
		memcpy(pc, pp, headPayload); pc += headPayload; pp += headPayload;
		// 擦除
		flash_erase(headPageAddr - MCU_FLASH_BEGIN_ADDRESS, MCU_FLASH_PAGE_SIZE);
		// 写入缓存
		flash_write(headPageAddr - MCU_FLASH_BEGIN_ADDRESS, MCU_FLASH_PAGE_SIZE, cache); pc = cache;
		
		// 中间页首地址
		uint32_t bodyPageAddr = headPageAddr + MCU_FLASH_PAGE_SIZE;
		// 中间页数据总长度
		uint32_t bodySize = tailPageAddr - bodyPageAddr;
		// 判断是否有中间页
		if(bodySize) {
			// 判断净荷是否也是在 SFFS 存储区中
			if(mcu_flash_page_addr((uint32_t)pp)) {
				// 净荷在 SFFS 中，必须一次写一页，否则可能有问题
				for(; bodyPageAddr < tailPageAddr; bodyPageAddr += MCU_FLASH_PAGE_SIZE) {
					// 净荷写入页缓存
					memcpy(cache, pp, MCU_FLASH_PAGE_SIZE); pp += MCU_FLASH_PAGE_SIZE;
					// 擦除
					flash_erase(bodyPageAddr - MCU_FLASH_BEGIN_ADDRESS, MCU_FLASH_PAGE_SIZE);
					// 写入缓存
					flash_write(bodyPageAddr - MCU_FLASH_BEGIN_ADDRESS, MCU_FLASH_PAGE_SIZE, cache);
				}
			} else {
				// 净荷不是在 SFFS 中，可以一次性写入中间页
				// 整体擦除
				flash_erase(bodyPageAddr - MCU_FLASH_BEGIN_ADDRESS, bodySize);
				// 整体写入
				flash_write(bodyPageAddr - MCU_FLASH_BEGIN_ADDRESS, bodySize, pp); pp += bodySize;
			}
		}
		
		// 写入尾部页
		// 净荷尾部写入页缓存
		memcpy(pc, pp, tailPayload); pc += tailPayload; pp += tailPayload;
		// 读入尾部的 dummy 数据到页缓存
		memcpy(pc, (void*)tailAddr, tailDummy); pc += tailDummy;
		// 擦除
		flash_erase(tailPageAddr - MCU_FLASH_BEGIN_ADDRESS, MCU_FLASH_PAGE_SIZE);
		// 写入缓存
		flash_write(tailPageAddr - MCU_FLASH_BEGIN_ADDRESS, MCU_FLASH_PAGE_SIZE, cache); pc = cache;
	}
	
	// 释放页缓存
	osFree(cache);
	// 恢复中断状态
	osCriticalLeave(cs); // }}}	
	
	// 返回写入的数据的有效长度
	return pp - (uint8_t*)payload;
}

uint16_t mcu_get_vbat(void) {
	struct adc_cfg_t cfg;
	uint16_t result, ref_vol;

	memset((void*)&cfg, 0, sizeof(cfg));
	cfg.src = ADC_TRANS_SOURCE_VBAT;
	cfg.ref_sel = ADC_REFERENCE_INTERNAL;
	cfg.int_ref_cfg = ADC_INTERNAL_REF_1_2;
	cfg.clk_sel = ADC_SAMPLE_CLK_24M_DIV13;
	cfg.clk_div = 0x3f;
	adc_init(&cfg);
	adc_enable(NULL, NULL, 0);

	adc_get_result(ADC_TRANS_SOURCE_VBAT, 0, &result);
	ref_vol = adc_get_ref_voltage(ADC_REFERENCE_INTERNAL);
	
	// !!! 注意：ADC 用完了之后一定要关闭掉，否则会导致关机功耗达到 3mA 以上 !!1
	adc_disable();
	return (result * 4 * ref_vol) / 1024;
}

void mcu_watchdog_init(uint32_t timeout) {
    //ool_write(PMU_REG_SYSTEM_STATUS, BSP_REBOOT_WATCHDOG);
    //wdt_init(WDT_ACT_CALL_IRQ, APP_WATCHDOG_DELAY);
    wdt_init(WDT_ACT_RST_CHIP, timeout/1000);
    wdt_start();
}

void mcu_watchdog_stop(void) {
    wdt_stop();
}

void mcu_watchdog_feed(void) {
    wdt_feed();
}
